/*
 * This file contains all the database manipulation functions.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "snmpstats_db.h"

#include "../../str.h"
#include "../../dprint.h"
#include "../../lib/srdb1/db.h"
#include "QuantumSipServerNotifications.h"

#define SET_STRING_VALUE(_val, _str)\
	do {\
			VAL_STR((_val)).s 		= (_str);\
			VAL_STR((_val)).len 	= strlen((_str));\
	} while(0);

/*
 * DB strings
 */
str db_url = str_init(DEFAULT_DB_URL);

str table_dialog_records = str_init(TABLE_DIALOG_RECORDS);
str table_dialog            = str_init(TABLE_DIALOG);
str table_missed_calls      = str_init(TABLE_MISSED_CALLS);

str column_hashEntry        = str_init(COLUMN_HASH_ENTRY);
str column_hashId           = str_init(COLUMN_HASH_ID);

str column_callId           = str_init(COLUMN_CALL_ID);
str column_callerId         = str_init(COLUMN_CALLER_ID);
str column_callerProxyId    = str_init(COLUMN_CALLER_PROXY_ID);
str column_calleeId         = str_init(COLUMN_CALLEE_ID);
str column_calleeProxyId    = str_init(COLUMN_CALLEE_PROXY_ID);
str column_callType         = str_init(COLUMN_CALL_TYPE);
str column_callState        = str_init(COLUMN_CALL_STATE);

str column_startTime        = str_init(COLUMN_START_TIME);
str column_endTime          = str_init(COLUMN_END_TIME);

str column_sip_code         = str_init(COLUMN_SIP_CODE);

/*
 *  db functions api
 */
db_func_t  db_functions;

/*
 * This function is used to persist dialog infomation.
 * It will be called only if the exported parameter in snmpstats is set enabled.
 */
/*
void CurrentDialogTable_db_add(CurrentDialogTable_context_str *context) {

   LM_INFO("---- PERSISTANCE ----");
   LM_INFO("HashEntry=%d, HashId=%d", context->HashEntry, context->HashId);
   LM_INFO("CallId=%s, CallType=%s, CallState=%s", context->CallId, context->CallType, context->CallState);
   LM_INFO("Caller=%s@%s, Callee=%s@%s",context->CallerId,context->CallerProxyId,context->CalleeId,context->CalleeProxyId);
   LM_INFO("*********************");

   // Bind the database module
   if (db_bind_mod(&db_url, &db_functions)) {
       LM_ERR("failed to bind database module\n");
       return;
   }

   // Initialize connection to DB
   db1_con_t *db_connection = db_functions.init(&db_url);
   if (!db_connection) {
       LM_ERR("failed to connect database\n");
       return;
   }

   // Use table
   if (db_functions.use_table(db_connection, &table_dialog_records) < 0) {
       LM_ERR("Error occured in use_table\n");
       return;
   }

   // column name construction
   db_key_t table_keys[SNMPSTATS_DIALOGS_COLUMN_COUNT] = {
       &column_hashEntry, &column_hashId,
       &column_callId,
       &column_callerId,  &column_callerProxyId,
       &column_calleeId,  &column_calleeProxyId,
       &column_callType,  &column_callState
   };

   // column type & value affections
   db_val_t table_values[SNMPSTATS_DIALOGS_COLUMN_COUNT];

   // TYPE = Integer
   VAL_TYPE(table_values) =
       VAL_TYPE(table_values+1) =
           VAL_TYPE(table_values+3) =
               VAL_TYPE(table_values+4) =
                   VAL_TYPE(table_values+5) =
                       VAL_TYPE(table_values+6) =
                           VAL_TYPE(table_values+7) =
                               VAL_TYPE(table_values+8) = DB1_INT;


   // TYPE = String
   VAL_TYPE(table_values+2) = DB1_STR;

   // Non-NULL flag
   VAL_NULL(table_values) =
       VAL_NULL(table_values+1) =
           VAL_NULL(table_values+2) =
               VAL_NULL(table_values+3) =
                   VAL_NULL(table_values+4) =
                       VAL_NULL(table_values+5) =
                           VAL_NULL(table_values+6) =
                               VAL_NULL(table_values+7) =
                                   VAL_NULL(table_values+8) = 0;


   // VALUE Integer
   VAL_INT(table_values) = context->HashEntry;
   VAL_INT(table_values+1) = context->HashId;
   VAL_INT(table_values+3) = atoi(context->CallerId);
   VAL_INT(table_values+4) = atoi(context->CallerProxyId);
   VAL_INT(table_values+5) = atoi(context->CalleeId);
   VAL_INT(table_values+6) = atoi(context->CalleeProxyId);
   VAL_INT(table_values+7) = atoi(context->CallType);
   VAL_INT(table_values+8) = atoi(context->CallState);



   // VALUE String
   SET_STRING_VALUE(table_values+2, context->CallId);

   // finally we are ready to query db
   if (0 != db_functions.insert_update(db_connection, table_keys, table_values, SNMPSTATS_DIALOGS_COLUMN_COUNT)) {
       LM_ERR("Error occured while persisting dialog.\n");
   }



   if(atoi(context->CallState) == 1)   // Created

   else if (atoi(context->CallState) >= 4) // Terminated or failed
   VAL_INT(table_values+10) = context->timestamp;

   db_key_t table_keys[3] = { &column_hashEntry, &column_hashId, &column_startTime};

   db_val_t table_values[3];

   // TYPE = Integer
   VAL_TYPE(table_values) = VAL_TYPE(table_values+1) = DB1_INT;
   VAL_TYPE(table_values+2) = DB1_DATETIME;

   // Non-NULL flag
   VAL_NULL(table_values) =
       VAL_NULL(table_values+1) =
           VAL_NULL(table_values+2) = 0;

   // VALUE Integer
   VAL_INT(table_values) = context->HashEntry;
   VAL_INT(table_values+1) = context->HashId;

   time_t now;
   time(&now);
   table_values[2]->val.time_val = now;

   // finally we are ready to query db
   if (0 != db_functions.insert_update(db_connection, table_keys, table_values, 3)) {
       LM_ERR("Error occured while persisting dialog.\n");
   }

   db_functions.close(db_connection);
}
*/

/*
 * This function is deprecated. Now dialog data is passed to the
 * snmp list through shared memory.
 *
 * This function retreives data from 'snmpstats_dialogs' TABLE and puts them into
 * a CurrentDialogTable_context_str structure given on parameter.
 */
int CurrentDialogTable_db_get(CurrentDialogTable_context_str *context) {

    db1_res_t* db_result = NULL; // results pointer

    /* Bind the database module */
    if (db_bind_mod(&db_url, &db_functions)) {
        LM_ERR("failed to bind database module\n");
        return -1;
    }

    /* Initialize connection to DB */
    db1_con_t *db_connection = db_functions.init(&db_url);
    if (!db_connection) {
        LM_ERR("failed to connect database\n");
        return -1;
    }

    /* Use table */
    if (db_functions.use_table(db_connection, &table_dialog_records) < 0) {
        LM_ERR("Error occured in use_table\n");
        return -1;
    }

    // column name construction
    db_key_t result_columns[SNMPSTATS_DIALOGS_COLUMN_COUNT] = {
        &column_hashEntry, &column_hashId,
        &column_callId,
        &column_callerId,  &column_callerProxyId,
        &column_calleeId,  &column_calleeProxyId,
        &column_callType,  &column_callState
    };

    db_key_t match_keys[1];
    match_keys[0] = &column_callState;

    db_val_t match_values[1];
    match_values[0].type = DB1_INT;
    match_values[0].nul = 0;
    match_values[0].val.int_val = 4;



    const db_op_t op = "<";

    // finally ready for the query
    if(0 != db_functions.query(db_connection,
                               match_keys,                      // key
                               &op,                             // operator
                               match_values,                    // value
                               result_columns,                  //columns in which we are interested.
                               1,                               //number of key-value pair
                               SNMPSTATS_DIALOGS_COLUMN_COUNT,  //number of column
                               NULL,                            // order
                               &db_result                       // results
                              )
      ) {
        LM_ERR("failed to query database\n");
        db_functions.close(db_connection);
        return -1;
    }

    // result check
    if (RES_ROW_N(db_result)<=0 || RES_ROWS(db_result)[0].values[0].nul != 0) {
        LM_INFO("CurrentDialogTable_db_get: no value found\n");
        if (db_result != NULL && db_functions.free_result(db_connection, db_result) < 0)
            LM_INFO("failed to free the result\n");
        db_functions.close(db_connection);
        return -1;
    }

    /* Handle result */
    LM_INFO("%d row(s) fetched.", RES_ROW_N(db_result));
    int i = 0;
    for(i = 0; i <RES_ROW_N(db_result); i++) {
        LM_INFO("callId = %s", (char*)RES_ROWS(db_result)[i].values[2].val.string_val);
    }
    /* Free the result */
    db_functions.free_result(db_connection, db_result);
    db_functions.close(db_connection);

    return 0;
}

/*
 * Retreive active dialog list from dialog TABLE in DB
 */
int CurrentDialogTable_db_get_dialog_list(char ***results) {


    db1_res_t* db_result = NULL; // results pointer

    /* Bind the database module */
    if (db_bind_mod(&db_url, &db_functions)) {
        LM_ERR("failed to bind database module\n");
        return -1;
    }
    /* Initialize connection to DB */
    db1_con_t *db_connection = db_functions.init(&db_url);
    if (!db_connection) {
        LM_ERR("failed to connect database\n");
        return -1;
    }

    /* Use table */
    if (db_functions.use_table(db_connection, &table_dialog) < 0) {
        LM_ERR("Error occured in use_table()\n");
        return -1;
    }

    // column name construction
    db_key_t result_columns[1];
    result_columns[0] = &column_callId;

    // finally ready for the query
    if(0 != db_functions.query(db_connection,
                               NULL,                      // key
                               NULL,                             // operator
                               NULL,                    // value
                               result_columns,                  //columns in which we are interested.
                               0,                               //number of key-value pair
                               1,  //number of column
                               NULL,                            // order
                               &db_result                       // results
                              )
      ) {
        LM_ERR("failed to query database\n");
        db_functions.close(db_connection);
        return -1;
    }

    // result check
    if (RES_ROW_N(db_result)<=0 || RES_ROWS(db_result)[0].values[0].nul != 0) {
        //LM_INFO("CurrentDialogTable_db_get_dialog_list: no value found\n");
        if (db_result != NULL && db_functions.free_result(db_connection, db_result) < 0)
            LM_INFO("failed to free the result\n");
        db_functions.close(db_connection);
        return -1;
    }

    int resultCount = RES_ROW_N(db_result);


    /* Handle result */
    // LM_INFO("%d row(s) fetched.", RES_ROW_N(db_result));

    *results = (char **)shm_malloc(RES_ROW_N(db_result) * sizeof(char *));

    int i = 0;
    for(i = 0; i < RES_ROW_N(db_result); i++) {
        // LM_INFO("callId = %s", (char*)RES_ROWS(db_result)[i].values[0].val.string_val);
        (*results)[i] = (char *)shm_malloc(40 * sizeof(char));
        strcpy((*results)[i], (char*)RES_ROWS(db_result)[i].values[0].val.string_val);
    }

    // Finally frees the memory */
    db_functions.free_result(db_connection, db_result);
    db_functions.close(db_connection);

    return resultCount;
}

/*
 * Retreives dialog's failure type from missed_calls TABLE in DB.
 */
char dialog_db_get_failure_type(const char *callId) {
    LM_INFO("Querying failure type:");
    db1_res_t* db_result = NULL; // results pointer

    /* Bind the database module */
    if (db_bind_mod(&db_url, &db_functions)) {
        LM_ERR("failed to bind database module\n");
        return -1;
    }
    /* Initialize connection to DB */
    db1_con_t *db_connection = db_functions.init(&db_url);
    if (!db_connection) {
        LM_ERR("failed to connect database\n");
        return -1;
    }

    /* Use table */
    if (db_functions.use_table(db_connection, &table_missed_calls) < 0) {
        LM_ERR("Error occured in use_table()\n");
        return -1;
    }

    // finally ready for the query
    char buffer[100];
    memset(buffer, 0, sizeof(buffer));
    sprintf(buffer, "SELECT sip_code FROM missed_calls WHERE callid='%s'", callId);
    // INFO("BUFFER=[%s]", buffer);
    str sql_str;
    sql_str.s = buffer;
    sql_str.len = strlen(buffer);

    db_functions.raw_query(db_connection, &sql_str, &db_result);

    // result check
    if (RES_ROW_N(db_result)<=0 || RES_ROWS(db_result)[0].values[0].nul != 0) {
        LM_INFO("No failure type found.\n");
        if (db_result != NULL && db_functions.free_result(db_connection, db_result) < 0)
            LM_INFO("failed to free the result\n");
        db_functions.close(db_connection);
        return '7';
    }

    /* Handle result */
    int sip_code;
    sip_code = atoi((char*)RES_ROWS(db_result)[0].values[0].val.string_val);
    LM_INFO("Result found: callId=%s, sip_code=%d", callId, sip_code);

    char result;
    switch (sip_code) {
    case 408:  // Request Timeout
        result = '5';
        break;
    case 486:  // Busy Here
        result = '6';
        break;
    case 500:  // Server Internal Error (RS failed)
        result = '8';
        break;
    default:
        result = '7';
    }

    // Finally frees the memory */
    db_functions.free_result(db_connection, db_result);
    db_functions.close(db_connection);

    return result;
}
